# pos.py
# This code written by Slowchop Studios
# http://slowchop.com/index.php/2006/07/15/a-fast-python-vector-class/
# http://roids.slowchop.com/roids/browser/trunk/src/pos.py
# Accessed 17th May 2007

# Changes from original:
#   __init__ now allows zero, one or two parameters:
#        -  Default (0,0)
#        -  A single parameter is assumed to be a tuple or another vector
#        -  Two parameters are assumed to be an x and y
#        -  Raises error if too many arguments.
#   normalise no longer crashes for zero vectors
#        -  Vector remains zero
#        -  Returns self
#   get_item is now uncommented
#   New methods:
#     dot(a,b)    - the dot product of two vectors
#     cross(a,b)  - the scalar cross product of two vectors
#     Pos.zero( ) - reset the vector to (0,0)
#     Pos.trunc( )- turns float x,y to int x,y and returns self
#     Pos.round( )- turns float x,y to rounded int x,y and returns self
#     Pos.force_float( )
#                 - forces x, y into float values

from random import randint, random
import math

def dot(a,b):
        return a.x * b.x + a.y * b.y

def cross(a,b):
        return a.x * b.y - a.y * b.x

class Pos(object):
	__slots__ = ('x', 'y') 
	def __init__(self, *args):
                if len( args ) == 0:
                        self.x, self.y = (0,0)
                        return
                if len( args ) == 1:
                        self.x, self.y = args[0][0], args[0][1]
                        return
                if len( args ) == 2:
        		self.x, self.y = args[0], args[1]
        		return
        	print "Error: Too many parameters sent to Pos.__init__!"
        	raise SystemExit
	def __str__(self):
		return '(%.1f,%.1f)' % (self.x, self.y)
	def __repr__(self):
		return str(self)
	def __eq__(self, o):
		return self.x == o[0] and self.y == o[1]
	def __cmp__(self, o):
		return self.__eq__(o)
	def __ne__(self, o):
		return not self.__eq__(o)
	def __hash__(self):
		return self.x + self.y * 10000
	def __lt__(self, o):
		if self.x < o[0]:
			return True
		if self.y < o[1]:
			return True
	def __add__(self, o):
		return Pos(self.x + o[0], self.y + o[1])
	def __sub__(self, o):
		return Pos(self.x - o[0], self.y - o[1])
	def __div__(self, o):
		return Pos(self.x / o, self.y / o)
	def __mul__(self, o):
		return Pos(self.x * o, self.y * o)
	def __neg__(self):
		return Pos(-self.x, -self.y)
	def __getitem__(self, a):
		if not a: return self.x
		return self.y
#	def __setitem__(self, a, v):
#		if a == 0: self.x = v
#		if a == 1: self.y = v
	def length(self):
		return math.hypot(self.x, self.y)
	def normalise(self):
		length = math.hypot(self.x, self.y)
		if length == 0: return self
		self.x = self.x / length
		self.y = self.y / length
		return self
	def zero( self ):
                self.x, self.y = (0,0)
        def trunc( self ):
                self.x, self.y = int(self.x), int(self.y)
                return self
        def round( self ):
                self.x, self.y = round(self.x), round(self.y)
                return self.trunc( )
        def force_float( self ):
                self.x = float( self.x )
                self.y = float( self.y )
	@staticmethod
	def random(xrange,yrange):
		return Pos(randint(xrange[0], xrange[1]), randint(yrange[0], yrange[1]))
	@staticmethod
	def random_range(center, max_radius):
		a = random() * math.pi * 2
		r = random() * max_radius
		return Pos(center.x + math.cos(a) * r, center.y + math.sin(a) * r)
	@staticmethod
	def list(l):
		return Pos(l[0], l[1])
	def to_list(self):
		return [self.x, self.y]
if __name__ == '__main__':
	import timeit
	t = timeit.Timer('b = a.y', 'from pos import Pos\na = Pos(5.3, 6.2)')
	print min(t.repeat(5, 1000000))
	# getitem is 10 times slower then attribute access!

